/**
 * 
 */
package designpattern.example.pos.barpay;

import java.util.Arrays;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.google.gson.Gson;

/**
 *
 *@类名称：BasePayApiTemplet.java
 *@类描述：基础支付api模板
 *@创建人：zhongy
 *@创建时间：2017年5月16日-下午5:48:45
 *@修改备注:
 *@version 
 */
public abstract class BasePayApiTemplet {
	static Logger log = Logger.getLogger(BasePayApiTemplet.class);
	private static final Gson util=new Gson();
	
	private TempletConfig config;//模板级参数 XXX 必须配置
	public TempletConfig getConfig() {
		return config;
	}
	public void setConfig(TempletConfig config) {
		this.config = config;
	}
	/**
	 * 
	 * 构思
	 * 1、支付上游的相关业务逻辑基本相同，所以想采用模板方式来实现核心框架型的代码，以提高开发效率
	 * 2、采用模板设计模式原型的原因：
	 * （1）、支付方法的框架，基本不变
	 * （2）、查询方法的框架，基本不变
	 * （3）、支付返回值返回解析的方式，基本不变  支付成功  支付失败  处理中 TODO 需要在base方法中自己实现
	 * 3、综上，决定对2中的相关方法进行模板化封装	
	 * 
	 * 粒度
	 * 1、只处理和上游支付接口的业务逻辑，不处理和自身平台的业务逻辑
	 * 
	 * 
	 * 支付方法中传递出异常外部进行相关的处理
	 * 
	 * 1、支付
	 * （1）、效验参数方法：如果外部进行了效验保证参数不为空，可以实现该方法
	 * （2）、支付返回值：必须封装为模板的返回值（成功、失败、存疑）；
	 * a、
	 * 2、查询
	 * 3、撤销
	 * 
	 * 
	 * 
	 */
	//支付(返回的结果 code 必须是我方封装的)
	public abstract TradeResult basePay(Object params) throws Exception;
	//效验支付参数 true 参数合法  false 参数不合法
	public abstract boolean checkPayParams(Object params);//XXX 这一步的参数效验是否放到外层进行处理
	//支付参数转化为查询参数
	public abstract Object payParamsToQueryParams(Object payParams);
	
	//查询
	public abstract TradeResult baseQuery(Object params)throws Exception;
	//查询参数效验 true 参数合法  false 参数不合法
	public abstract boolean checkQueryParams(Object params);//XXX 这一步的参数效验是否放到外层进行处理
	
	
	
	
	//撤销订单
	public abstract TradeResult baseCancel(Object params)throws Exception;
	//撤销参数效验
	public abstract boolean checkCancelParams(Object params);//XXX 这一步的参数效验是否放到外层进行处理
	
	
	//退款
	public abstract TradeResult baseRefund(Object params)throws Exception;
	//退款参数效验
	public abstract boolean checkRefundParams(Object params);//XXX 这一步的参数效验是否放到外层进行处理
	
	
	
	
	
	//模板支付
	public TradeResult pay(Object params,String logPre){
		/**
		 * step1、支付参数的相关效验
		 * step2、进行支付
		 * step3、返回值解析
		 * step4、异常解决
		 * step5、返回值返回
		 */
		TradeResult result=null;
		//step1、支付参数的相关效验
		if(!checkPayParams(params) || config==null){//XXX  这一步是否需要在这里进行实现
			log.info(logPre+"必要参数为空");
			return new TradeResult(TradeResult.CODE_PAY_FAIL,"必要参数为空");
		}
		try{
			log.info(logPre+"请求-"+util.toJson(params));
			result= basePay(params);
			log.info(logPre+"返回值-"+util.toJson(result));
			if(result==null||TradeResult.CODE_PAY_PROCESS.equals(result.getCode())){// XXX 调用查询
				log.info(logPre+"订单处于支付中调用查询");
				result=query(payParamsToQueryParams(params), 1,logPre);//TODO  支付的参数如何平滑的过渡到查询的参数
			}
			
		}catch(Exception e){
			e.printStackTrace();
			// 异常情况调用查询
			log.info(logPre+"支付捕获异常进行查询"+e.getMessage());
			result=query(payParamsToQueryParams(params), 1,logPre);
		}
		//XXX 参数解析
		//XXX 明确支付结果的返回+中间状态的对订单进行作废
		if(result==null||TradeResult.CODE_PAY_PROCESS.equals(result.getCode())){//异常+中间状态的取消	
			cancel(params, 0,logPre);
		}
		
		
		return result;
	}
	
	//模板查询 (目前只支持支付查询)
	public TradeResult query(Object params,int retryCounts,String logPre){//其他支付的参数
		/**
		 * step1、参数效验
		 * step2、调用查询
		 * step3、返回值解析（异常+等待支付的 继续查询）
		 * 
		 * 
		 */
		TradeResult result=null;
		if(retryCounts!=1){//不是第一次
			try {
				Thread.sleep(config.getQueryInterval());
			} catch (InterruptedException e) {
				log.info(logPre+"查询线程休息异常"+e.getMessage());
				e.printStackTrace();
			}
		}
		if(retryCounts>config.getQueryTimes()){//超过最大次数了
			log.info(logPre+"交易时间过长");
			return new TradeResult(TradeResult.CODE_PAY_PROCESS,"交易时间过长");//XXX  一般发生在：1、网络多次超时 2、交易一直未完成
		}
		if(!checkQueryParams(params)){
			log.info(logPre+"查询参数不合法");
			return new TradeResult(TradeResult.CODE_PAY_FAIL,"查询参数不合法");//XXX  一般发生在：1、联调阶段的错误 正式上线一般不会发生
		}
		try{
			log.info(logPre+"递归查询第"+retryCounts+"次");
			result=baseQuery(params);
			if(result==null||StringUtils.isBlank(result.getCode())||Arrays.asList(TradeResult.doubtfulArr).contains(result.getCode())){
				return query(params, ++retryCounts,logPre);
			}
		}catch(Exception e){
			e.printStackTrace();
			log.info("查询异常："+e.getMessage());
			return query(params, ++retryCounts,logPre);
		}
		return result;
	}
	
	//模板撤销（异常的时候不考虑查询）
	public TradeResult cancel(Object params,int retryCounts,String logPre){
		TradeResult result=null;
		if(retryCounts>config.getCancelTimes()){//超过最大次数了
			log.info("");
			return new TradeResult("交易时间过长");
		}
		if(!checkCancelParams(params)){
			return new TradeResult("参数不合法");
		}
		try{
			result=baseCancel(params);
			if(result==null||StringUtils.isBlank(result.getCode())||Arrays.asList(TradeResult.doubtfulArr).contains(result.getCode())){
				return cancel(params, ++retryCounts,logPre);
			}
		}catch(Exception e){//FIXME 目前不考虑调用查询接口
			e.printStackTrace();
			log.info("撤销异常："+e.getMessage());
			return cancel(params, ++retryCounts,logPre);
		}
		return result;
		
		
		
	}
	
	
	

}
